package edu.northwestern.cbits.purple_robot_manager.probes.sensors; import android.content.Context; import android.content.SharedPreferences; import android.hardware.Sensor; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.preference.CheckBoxPreference; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.Map; import edu.northwestern.cbits.purple_robot_manager.R; import edu.northwestern.cbits.purple_robot_manager.activities.settings.FlexibleListPreference; import edu.northwestern.cbits.purple_robot_manager.logging.LogManager; import edu.northwestern.cbits.purple_robot_manager.probes.Probe; import edu.northwestern.cbits.purple_robot_manager.util.WakeLockManager; public abstract class SensorProbe extends Probe implements SensorEventListener { public static final String SENSOR_MAXIMUM_RANGE = "MAXIMUM_RANGE"; public static final String SENSOR_NAME = "NAME"; public static final String SENSOR_POWER = "POWER"; public static final String SENSOR_TYPE = "TYPE"; public static final String SENSOR_VENDOR = "VENDOR"; public static final String SENSOR_VERSION = "VERSION"; public static final String SENSOR_RESOLUTION = "RESOLUTION"; public static final String BUNDLE_SENSOR = "SENSOR"; public static final String SENSOR_ACCURACY = "ACCURACY"; public static final String EVENT_TIMESTAMP = "EVENT_TIMESTAMP"; public static final String SENSOR_TIMESTAMP = "SENSOR_TIMESTAMP"; public static final String NORMALIZED_TIMESTAMP = "NORMALIZED_TIMESTAMP"; public static final String X = "X"; public static final String Y = "Y"; public static final String Z = "Z"; private static final String PROBE_WAKELOCK = "wakelock"; private static final boolean DEFAULT_ENABLED = false; public static final String DEFAULT_SENSOR_FREQUENCY = "0"; public static final String DEFAULT_SAMPLE_FREQUENCY = "300000"; private static final String DEFAULT_WAKELOCK = "-1"; private static final String DEFAULT_DURATION = "0"; private int _lastFrequency = -1; private int _lastDuration = -1; private Handler _handler = null; public Context _context = null; private PowerManager.WakeLock _wakeLock = null; private int _wakeLockLevel = -1; private boolean _running = false; private long _lastRun = 0; public abstract String getPreferenceKey(); public String probeCategory(Context context) { return context.getString(R.string.probe_sensor_category); } @Override public void enable(Context context) { String key = this.getPreferenceKey(); SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putBoolean("config_probe_" + key + "_enabled", true); e.commit(); } @Override public void disable(Context context) { String key = this.getPreferenceKey(); SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putBoolean("config_probe_" + key + "_enabled", false); e.commit(); } @Override public JSONObject fetchSettings(Context context) { JSONObject settings = super.fetchSettings(context); try { int sensorFrequencyValues = this.getResourceSensorFrequencyValues(); if (sensorFrequencyValues != -1) { JSONObject frequency = new JSONObject(); frequency.put(Probe.PROBE_TYPE, Probe.PROBE_TYPE_LONG); JSONArray values = new JSONArray(); String[] options = context.getResources().getStringArray(sensorFrequencyValues); for (String option : options) { values.put(Long.parseLong(option)); } frequency.put(Probe.PROBE_VALUES, values); settings.put(Probe.PROBE_FREQUENCY, frequency); } int sampleFrequencyValues = this.getResourceSampleFrequencyValues(); if (sampleFrequencyValues != -1) { JSONObject frequency = new JSONObject(); frequency.put(Probe.PROBE_TYPE, Probe.PROBE_TYPE_LONG); JSONArray values = new JSONArray(); String[] options = context.getResources().getStringArray(sampleFrequencyValues); for (String option : options) { values.put(Long.parseLong(option)); } frequency.put(Probe.PROBE_VALUES, values); settings.put(Probe.PROBE_SAMPLE_FREQUENCY, frequency); } int durationValues = this.getResourceDurationValues(); if (durationValues != -1) { JSONObject duration = new JSONObject(); duration.put(Probe.PROBE_TYPE, Probe.PROBE_TYPE_LONG); JSONArray values = new JSONArray(); String[] options = context.getResources().getStringArray(durationValues); for (String option : options) { values.put(Long.parseLong(option)); } duration.put(Probe.PROBE_VALUES, values); settings.put(Probe.PROBE_DURATION, duration); } JSONObject wakelock = new JSONObject(); wakelock.put(Probe.PROBE_TYPE, Probe.PROBE_TYPE_LONG); JSONArray values = new JSONArray(); String[] options = context.getResources().getStringArray(R.array.wakelock_values); for (String option : options) { values.put(Double.parseDouble(option)); } wakelock.put(Probe.PROBE_VALUES, values); settings.put(SensorProbe.PROBE_WAKELOCK, wakelock); } catch (JSONException e) { LogManager.getInstance(context).logException(e); } return settings; } private int getResourceDurationValues() { return R.array.probe_duration_with_none_values; } private int getResourceDurationLabels() { return R.array.probe_duration_with_none_labels; } public int getResourceSensorFrequencyLabels() { return R.array.probe_continuous_frequency_labels; } public int getResourceSensorFrequencyValues() { return R.array.probe_continuous_frequency_values; } public int getResourceSampleFrequencyLabels() { return R.array.probe_sample_frequency_labels; } public int getResourceSampleFrequencyValues() { return R.array.probe_sample_frequency_values; } @Override public void updateFromMap(Context context, Map<String, Object> params) { super.updateFromMap(context, params); if (params.containsKey(Probe.PROBE_FREQUENCY)) { Object frequency = params.get(Probe.PROBE_FREQUENCY); if (frequency instanceof Long || frequency instanceof Integer) { String key = "config_probe_" + this.getPreferenceKey() + "_sensor_frequency"; SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putString(key, frequency.toString()); e.commit(); } if (frequency instanceof Double) { String key = "config_probe_" + this.getPreferenceKey() + "_sensor_frequency"; SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putString(key, "" + ((Double) frequency).intValue()); e.commit(); } else if (frequency instanceof String) { String key = "config_probe_" + this.getPreferenceKey() + "_sensor_frequency"; SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putString(key, frequency.toString()); e.commit(); } } if (params.containsKey(Probe.PROBE_SAMPLE_FREQUENCY)) { Object frequency = params.get(Probe.PROBE_SAMPLE_FREQUENCY); if (frequency instanceof Long || frequency instanceof Integer) { String key = "config_probe_" + this.getPreferenceKey() + "_sample_frequency"; SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putString(key, frequency.toString()); e.commit(); } if (frequency instanceof Double) { String key = "config_probe_" + this.getPreferenceKey() + "_sample_frequency"; SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putString(key, "" + ((Double) frequency).intValue()); e.commit(); } else if (frequency instanceof String) { String key = "config_probe_" + this.getPreferenceKey() + "_sample_frequency"; SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putString(key, frequency.toString()); e.commit(); } } if (params.containsKey(Probe.PROBE_DURATION)) { Object duration = params.get(Probe.PROBE_DURATION); if (duration instanceof Long || duration instanceof Integer) { String key = "config_probe_" + this.getPreferenceKey() + "_duration"; SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putString(key, duration.toString()); e.commit(); } if (duration instanceof Double) { String key = "config_probe_" + this.getPreferenceKey() + "_duration"; SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putString(key, "" + ((Double) duration).intValue()); e.commit(); } else if (duration instanceof String) { String key = "config_probe_" + this.getPreferenceKey() + "_duration"; SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putString(key, duration.toString()); e.commit(); } } if (params.containsKey(SensorProbe.PROBE_WAKELOCK)) { Object wakelock = params.get(SensorProbe.PROBE_WAKELOCK); if (wakelock instanceof Long || wakelock instanceof Integer) { String key = "config_probe_" + this.getPreferenceKey() + "_wakelock"; SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putString(key, "" + wakelock); e.commit(); } if (wakelock instanceof Double) { String key = "config_probe_" + this.getPreferenceKey() + "_wakelock"; SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putString(key, "" + wakelock); e.commit(); } else if (wakelock instanceof String) { String key = "config_probe_" + this.getPreferenceKey() + "_wakelock"; SharedPreferences prefs = Probe.getPreferences(context); SharedPreferences.Editor e = prefs.edit(); e.putString(key, "" + wakelock); e.commit(); } } } public PreferenceScreen preferenceScreen(Context context, PreferenceManager manager) { String key = this.getPreferenceKey(); PreferenceScreen screen = super.preferenceScreen(context, manager); screen.setTitle(this.title(context)); screen.setSummary(this.summary(context)); CheckBoxPreference enabled = new CheckBoxPreference(context); enabled.setTitle(R.string.title_enable_probe); enabled.setKey("config_probe_" + key + "_enabled"); enabled.setDefaultValue(SensorProbe.DEFAULT_ENABLED); screen.addPreference(enabled); FlexibleListPreference frequency = new FlexibleListPreference(context); frequency.setKey("config_probe_" + key + "_sensor_frequency"); frequency.setEntryValues(this.getResourceSensorFrequencyValues()); frequency.setEntries(this.getResourceSensorFrequencyLabels()); frequency.setTitle(R.string.probe_sensor_frequency_label); frequency.setDefaultValue(SensorProbe.DEFAULT_SENSOR_FREQUENCY); screen.addPreference(frequency); FlexibleListPreference sample = new FlexibleListPreference(context); sample.setKey("config_probe_" + key + "_sample_frequency"); sample.setEntryValues(this.getResourceSampleFrequencyValues()); sample.setEntries(this.getResourceSampleFrequencyLabels()); sample.setTitle(R.string.probe_frequency_label); sample.setDefaultValue(SensorProbe.DEFAULT_SAMPLE_FREQUENCY); screen.addPreference(sample); String[] sampValues = context.getResources().getStringArray(this.getResourceSampleFrequencyValues()); FlexibleListPreference duration = new FlexibleListPreference(context); duration.setKey("config_probe_" + key + "_duration"); duration.setEntryValues(this.getResourceDurationValues()); duration.setEntries(this.getResourceDurationLabels()); duration.setTitle(R.string.probe_duration_label); duration.setDefaultValue(SensorProbe.DEFAULT_DURATION); screen.addPreference(duration); FlexibleListPreference wakelock = new FlexibleListPreference(context); wakelock.setKey("config_probe_" + key + "_wakelock"); wakelock.setEntryValues(R.array.wakelock_values); wakelock.setEntries(R.array.wakelock_labels); wakelock.setTitle(R.string.probe_wakelock_title); wakelock.setSummary(R.string.probe_wakelock_summary); wakelock.setDefaultValue(SensorProbe.DEFAULT_WAKELOCK); screen.addPreference(wakelock); return screen; } @Override public Map<String, Object> configuration(Context context) { Map<String, Object> map = super.configuration(context); map.put(Probe.PROBE_FREQUENCY, this.getSensorFrequency()); map.put(Probe.PROBE_SAMPLE_FREQUENCY, this.getSampleFrequency()); map.put(Probe.PROBE_DURATION, this.getDuration()); map.put(SensorProbe.PROBE_WAKELOCK, this.getWakelock()); return map; } private int getSensorFrequency() { String key = this.getPreferenceKey(); SharedPreferences prefs = Probe.getPreferences(this._context); return Integer.parseInt(prefs.getString("config_probe_" + key + "_sensor_frequency", SensorProbe.DEFAULT_SENSOR_FREQUENCY)); } private int getSampleFrequency() { String key = this.getPreferenceKey(); SharedPreferences prefs = Probe.getPreferences(this._context); return Integer.parseInt(prefs.getString("config_probe_" + key + "_sample_frequency", SensorProbe.DEFAULT_SAMPLE_FREQUENCY)); } protected int getDuration() { String key = this.getPreferenceKey(); SharedPreferences prefs = Probe.getPreferences(this._context); return Integer.parseInt(prefs.getString("config_probe_" + key + "_duration", SensorProbe.DEFAULT_DURATION)); } private int getWakelock() { String key = this.getPreferenceKey(); SharedPreferences prefs = Probe.getPreferences(this._context); return Integer.parseInt(prefs.getString("config_probe_" + key + "_wakelock", SensorProbe.DEFAULT_WAKELOCK)); } @Override public boolean isEnabled(Context context) { final SensorManager sensors = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); final Sensor sensor = sensors.getDefaultSensor(this.getSensorType()); this._context = context.getApplicationContext(); if (super.isEnabled(context)) { SharedPreferences prefs = Probe.getPreferences(context); String key = this.getPreferenceKey(); boolean enabled = prefs.getBoolean("config_probe_" + key + "_enabled", SensorProbe.DEFAULT_ENABLED); int duration = Integer.parseInt(prefs.getString("config_probe_" + key + "_duration", SensorProbe.DEFAULT_DURATION)); int sampleFrequency = Integer.parseInt(prefs.getString("config_probe_" + key + "_sample_frequency", SensorProbe.DEFAULT_SAMPLE_FREQUENCY)); long now = System.currentTimeMillis(); if (enabled) { if (this._running == false) { int wakeLevel = Integer.parseInt(prefs.getString("config_probe_" + key + "_wakelock", SensorProbe.DEFAULT_WAKELOCK)); if (wakeLevel != this._wakeLockLevel) { if (this._wakeLock != null) { WakeLockManager.getInstance(context).releaseWakeLock(this._wakeLock); this._wakeLock = null; } this._wakeLockLevel = wakeLevel; } if (this._wakeLockLevel != -1 && this._wakeLock == null) this._wakeLock = WakeLockManager.getInstance(context).requestWakeLock(this._wakeLockLevel, this.getClass().getCanonicalName()); int frequency = Integer.parseInt(prefs.getString("config_probe_" + key + "_sensor_frequency", SensorProbe.DEFAULT_SENSOR_FREQUENCY)); if (this._lastFrequency != frequency || this._lastDuration != duration || (now - this._lastRun > sampleFrequency && duration > 0)) { this._lastRun = now; // Log.e("PR-SENSOR", "STARTING SAMPLING " + new Date() + " -- " + sampleFrequency); this._lastFrequency = frequency; this._lastDuration = duration; sensors.unregisterListener(this, sensor); this.clearDataBuffers(); if (this._handler != null) { Looper loop = this._handler.getLooper(); loop.quit(); this._handler = null; } if (frequency != SensorManager.SENSOR_DELAY_FASTEST && frequency != SensorManager.SENSOR_DELAY_UI && frequency != SensorManager.SENSOR_DELAY_NORMAL) { frequency = SensorManager.SENSOR_DELAY_GAME; } final SensorProbe me = this; final int finalFrequency = frequency; Runnable r = new Runnable() { public void run() { Looper.prepare(); me._handler = new Handler(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) sensors.registerListener(me, sensor, finalFrequency, 0, me._handler); else sensors.registerListener(me, sensor, finalFrequency, me._handler); Looper.loop(); } }; Thread t = new Thread(r, key); t.start(); if (this._lastDuration > 0 && this._running == false) { // Log.e("PR-SENSOR", "SCHEDULING STOP " + new Date() + " -- " + this._lastDuration); this._running = true; Runnable stopCollection = new Runnable() { public void run() { try { Thread.sleep(me._lastDuration * 1000); } catch (InterruptedException e) { e.printStackTrace(); } sensors.unregisterListener(me, sensor); me.flushData(); if (me._handler != null) { Looper loop = me._handler.getLooper(); loop.quit(); me._handler = null; } if (me._wakeLock != null) { WakeLockManager.getInstance(me._context).releaseWakeLock(me._wakeLock); me._wakeLock = null; } me._running = false; // Log.e("PR-SENSOR", "STOPPED " + new Date()); } }; Thread stopThread = new Thread(stopCollection); stopThread.start(); } } } return true; } else { sensors.unregisterListener(this, sensor); this.flushData(); this._lastFrequency = -1; this._lastDuration = -1; if (this._handler != null) { Looper loop = this._handler.getLooper(); loop.quit(); this._handler = null; } if (this._wakeLock != null) { WakeLockManager.getInstance(context).releaseWakeLock(this._wakeLock); this._wakeLock = null; } } } else { sensors.unregisterListener(this, sensor); this.flushData(); this._lastFrequency = -1; this._lastDuration = -1; if (this._handler != null) { Looper loop = this._handler.getLooper(); loop.quit(); this._handler = null; } if (this._wakeLock != null) { WakeLockManager.getInstance(context).releaseWakeLock(this._wakeLock); this._wakeLock = null; } } return false; } protected abstract void flushData(); protected abstract int getSensorType(); protected abstract void clearDataBuffers(); public abstract void createDataBuffers(); public void transmitData(Sensor sensor, double timestamp) { Bundle data = new Bundle(); Bundle sensorBundle = new Bundle(); sensorBundle.putFloat(SensorProbe.SENSOR_MAXIMUM_RANGE, sensor.getMaximumRange()); sensorBundle.putString(SensorProbe.SENSOR_NAME, sensor.getName()); sensorBundle.putFloat(SensorProbe.SENSOR_POWER, sensor.getPower()); sensorBundle.putFloat(SensorProbe.SENSOR_RESOLUTION, sensor.getResolution()); sensorBundle.putInt(SensorProbe.SENSOR_TYPE, sensor.getType()); sensorBundle.putString(SensorProbe.SENSOR_VENDOR, sensor.getVendor()); sensorBundle.putInt(SensorProbe.SENSOR_VERSION, sensor.getVersion()); data.putDouble(Probe.BUNDLE_TIMESTAMP, timestamp); data.putString(Probe.BUNDLE_PROBE, this.name(this._context)); data.putBundle(SensorProbe.BUNDLE_SENSOR, sensorBundle); this.attachReadings(data); this.transmitData(this._context, data); } protected abstract void attachReadings(Bundle data); }